/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IMAGE_KNIFE_C_DOWNLOAD_INTERCEPTOR_OH_H
#define IMAGE_KNIFE_C_DOWNLOAD_INTERCEPTOR_OH_H

#include "imageknife_dispatcher.h"
#include "imageknife_task_internal.h"
#include "interceptor.h"
#include "imageknife_request.h"
#include "imageknife_data_napi.h"
#include "dns_option.h"
#include <future>
#include <stack>
#include "securec.h"
#include "imageknife_internal.h"
#include "hitrace/trace.h"
#include "task_worker.h"

namespace ImageKnifePro {

class DownloadInterceptorDefault : public LoadInterceptor {
public:
    DownloadInterceptorDefault()
    {
        name = "Default DownloadInterceptor";
        uint32_t errorCode = 0;
        session_ = HMS_Rcp_CreateSession(NULL, &errorCode);
        if (errorCode) {
            IMAGE_KNIFE_LOG(LOG_ERROR, "Rcp Create Session Failed, error code:%{public}d", errorCode);
            session_ = nullptr;
        }

        if (!IsDownloadDetachEnabled()) {
            IMAGE_KNIFE_LOG(LOG_WARN, "Default Download Interceptor Not Support Detach On Low API Device");
        }
    }

    bool Resolve(std::shared_ptr<ImageKnifeTask> task) override
    {
        std::string url;
        if (session_ == nullptr) {
            task->EchoError("Session Error");
            return false;
        }

        if (task->GetImageSource()->GetString(url)) {
            // 读本地资源任务，直接拒绝
            if (url.find("/data/storage") == 0 || url.find("file:") == 0 || url.find("data:image/") == 0) {
                return false;
            } else if (url.empty()) {
                task->EchoError("Empty Url");
                return false;
            }

            auto taskInternal = std::dynamic_pointer_cast<ImageKnifeTaskInternal>(task);
            return LoadImageFromUrl(url, taskInternal);
        }

        return false;
    }

    void Cancel(std::shared_ptr<ImageKnifeTask> task) override
    {
        auto taskInternal = std::static_pointer_cast<ImageKnifeTaskInternal>(task);
        taskInternal->cancelMutex_.lock();
        taskInternal->rcpRequestCanceled = true;
        auto rcpRequest = taskInternal->rcpRequest;
        taskInternal->rcpRequest = nullptr;
        taskInternal->cancelMutex_.unlock();

        if (rcpRequest != nullptr && session_ != nullptr) {
            sessionLock_.lock();
            uint32_t errorCode = HMS_Rcp_CancelRequest(session_, rcpRequest);
            sessionLock_.unlock();
            if (!errorCode) {
                IMAGE_KNIFE_LOG(LOG_DEBUG, "Cancel Rcp Download Success");
                task->cancelInfo = "Cancel Download Success";
            } else {
                task->EchoError("Cancel Download Failed, error code: " + std::to_string(errorCode));
            }
        }

        // rcp request还没被销毁，进行销毁
        if (rcpRequest != nullptr) {
            DestroyConfiguration(rcpRequest);
            HMS_Rcp_DestroyRequest(rcpRequest);
        }
    }

private:
    // Rcp 异步回调数据
    struct CallbackData {
        DownloadInterceptorDefault *downloadInterceptor = nullptr;
        std::shared_ptr<ImageKnifeTaskInternal> task = nullptr;

        // 仅在低版本ROM不支持分离时，进行异步转同步
        std::promise<bool> waitDownload;
    };

    std::stack<Rcp_Session *> sessionStack_;
    std::mutex sessionLock_;
    Rcp_Session *session_ = nullptr;

    Rcp_Session *GetRcpSession();
    void RecycleSession(Rcp_Session *session);

    static bool IsDownloadDetachEnabled();

    static void DestroyConfiguration(Rcp_Request *rcpRequest);

    Rcp_Headers *CreateRcpHeaders(const std::multimap<std::string, std::string> &head)
    {
        if (head.size() == 0) {
            return nullptr;
        }
        Rcp_Headers *rcpHeaders = HMS_Rcp_CreateHeaders();
        for (auto it = head.begin(); it != head.end(); it++) {
            HMS_Rcp_SetHeaderValue(rcpHeaders, it->first.c_str(), it->second.c_str());
        }
        return rcpHeaders;
    }

    bool LoadImageFromUrl(std::string url, std::shared_ptr<ImageKnifeTaskInternal> task)
    {
        auto rcpRequest = HMS_Rcp_CreateRequest(url.c_str());
        if (rcpRequest == nullptr) {
            task->EchoError("Create Rcp Request Failed");
            return false;
        }
        ConfigHttpRequest(task, rcpRequest);

        CallbackData *data = new CallbackData;
        data->task = task;
        data->downloadInterceptor = this;
        Rcp_ResponseCallbackObject callback = {ResponseCallback, data};

        sessionLock_.lock();
        OH_HiTrace_StartAsyncTrace("ImageKnifePro Rcp Fetch", task->traceId);
        uint32_t errCode = HMS_Rcp_Fetch(session_, rcpRequest, &callback);
        sessionLock_.unlock();
        if (errCode) {
            task->EchoError("HMS_Rcp_Fetch error:" + std::to_string(errCode));
            return false;
        }

        /**
         * 1.fetch完成后记录request
         *   避免config以及fetch执行中更改request状态，造成崩溃和未定义的结果
         * 2.此时fetch结束，如果已经调用过Cancel，因为Request为null没有起效，需要再调用一次(这次不阻塞主线程)
         * 3.两把锁分开操作，避免死锁
         *   sessionLock用于全局session接口调用，task->cancelMutex_用于单个任务取消
         * 4.此时如果再调Cancel，仍通过callback返回，所以最终还是进行Detach分离，以确保后续业务流程正常
         */
        task->cancelMutex_.lock();
        task->rcpRequest = rcpRequest;
        if (task->rcpRequestCanceled) {
            // 先解锁避免Cancel时重复获取锁
            task->cancelMutex_.unlock();
            Cancel(task);
        } else {
            task->cancelMutex_.unlock();
        }

        // Rcp fetch成功，进行分离
        if (IsDownloadDetachEnabled()) {
            Detach(task);
        } else {
            // 低版本设备Rom 不支持分离，等待下载过程结束
            bool res = data->waitDownload.get_future().get();
            delete data;
            return res;
        }

        return true;
    }

    void ConfigHttpRequest(std::shared_ptr<ImageKnifeTaskInternal> task, Rcp_Request *rcpRequest)
    {
        auto request = task->GetImageKnifeRequest();
        IMAGE_KNIFE_CHECK(request == nullptr);
        auto option = request->GetImageKnifeOption();
        IMAGE_KNIFE_CHECK(option == nullptr);

        rcpRequest->headers = CreateRcpHeaders(option->httpHeaders);
        if (rcpRequest->headers == nullptr) {
            rcpRequest->headers = CreateRcpHeaders(ImageKnifeInternal::GetInstance().GetGlobalHttpHeaders());
        }

        // 设置为Get请求
        rcpRequest->method = RCP_METHOD_GET;

        Rcp_Configuration *config = new Rcp_Configuration;
        IMAGE_KNIFE_CHECK(config == nullptr);
        (void)memset_s(config, sizeof(Rcp_Configuration), 0, sizeof(Rcp_Configuration));

        // 设置连接超时时间
        if (option->httpOption.connectTimeout > 0) {
            config->transferConfiguration.timeout.connectMs = option->httpOption.connectTimeout;
        }

        // 设置传输超时时间
        if (option->httpOption.readTimeout > 0) {
            config->transferConfiguration.timeout.transferMs = option->httpOption.readTimeout;
        }

        // 设置 CA证书
        if (option->httpOption.caPath.length() > 0) {
            int length = option->httpOption.caPath.length() + 1;
            char *filePath = static_cast<char *>(malloc(length));
            IMAGE_KNIFE_CHECK(filePath == nullptr);
            config->securityConfiguration.certificate.filePath = filePath;
            memset_s(config->securityConfiguration.certificate.filePath, length, 0x00, length);
            strcpy_s(config->securityConfiguration.certificate.filePath, length,
                     option->httpOption.caPath.c_str());
        }

        // 设置Dns, rcpDnsRule跟随dnsOptionInternal析构，避免重复创建
        auto dnsOptionInternal = std::dynamic_pointer_cast<DnsOptionInternal>(option->dnsOption);
        if (dnsOptionInternal  == nullptr) {
            dnsOptionInternal = ImageKnifeInternal::GetInstance().GetGlobalDnsOption();
        }
        if (dnsOptionInternal != nullptr) {
            config->dnsConfiguration.dnsRules = dnsOptionInternal->GetRcpDnsRule();
        }

        // 设置下载进度监听回调
        if (option->progressListener != nullptr && task->GetImageRequestType() == ImageRequestType::MAIN_SRC) {
            config->tracingConfiguration.httpEventsHandler.onDownloadProgress = {.callback = OnProgressCallback,
                .usrObject = &(option->progressListener)};
        }
        config->transferConfiguration.autoRedirect = true;
        rcpRequest->configuration = config;
    }

    static void OnProgressCallback(void* userObject, uint64_t totalSize, uint64_t transferredSize)
    {
        if (totalSize > 0) {
            std::function<void(double)>* userFunc = static_cast<std::function<void(double)>*>(userObject);
            (*userFunc)(static_cast<double>(transferredSize) / totalSize);
        }
    }

    static void ResponseCallback(void *usrCtx, Rcp_Response *response, uint32_t errCode)
    {
        const int httpDownloadCanceled = 1007900992;
        CallbackData *data = static_cast<CallbackData*>(usrCtx);
        IMAGE_KNIFE_CHECK(data == nullptr);
        IMAGE_KNIFE_CHECK(data->downloadInterceptor == nullptr);
        IMAGE_KNIFE_CHECK(data->task == nullptr);
        auto &task = data->task;
        OH_HiTrace_FinishAsyncTrace("ImageKnifePro Rcp Fetch", task->traceId);

        bool result = false;
        if (errCode && errCode != httpDownloadCanceled) {
            task->EchoError("HttpFetch HMS_Rcp_Fetch error:" + std::to_string(errCode));
        }

        if (response != nullptr) {
            task->httpCode = response->statusCode;
            if (response->statusCode != RCP_OK) {
                task->EchoError("HttpFetch response error, statusCode: " + std::to_string(response->statusCode));
                response->destroyResponse(response);
            } else {
                // 提供删除器避免错误析构下载数据buffer
                auto deleter = [response](void *) {
                    response->destroyResponse(response);
                };
                task->product.imageBuffer = std::shared_ptr<uint8_t []>((uint8_t *)response->body.buffer, deleter);
                task->product.imageLength = response->body.length;
                result = true;
            }
        }

        // 释放资源
        task->cancelMutex_.lock();
        auto rcpRequest = task->rcpRequest;
        task->rcpRequest = nullptr;
        task->cancelMutex_.unlock();

        // 如果请求被取消，则由取消后进行销毁
        if (rcpRequest != nullptr) {
            DestroyConfiguration(rcpRequest);
            HMS_Rcp_DestroyRequest(rcpRequest);
        }

        if (IsDownloadDetachEnabled()) {
            data->downloadInterceptor->OnComplete(task, result);
            delete data;
        } else {
            // 低版本ROM不支持分离，通知等待线程下载结束
            data->waitDownload.set_value(result);
        }
    }
};

};

#endif // IMAGE_KNIFE_C_DOWNLOAD_INTERCEPTOR_OH_H
